#pragma once
#include "geometry.hpp"

class particle_system {
public:
	const int TOTAL_COMPONENT_COUNT = 3 + 3 + 3 + 1; // position + color + vector + start time
	const int TOTAL_COMPONENT_BYTES = TOTAL_COMPONENT_COUNT * sizeof(float);

private:
	float *_particlearr;
	int _maxcount;
	int _curcount;
	int _nextparticle;
	GLuint _bo;

public:
	particle_system(int maxcount) 
		: _particlearr(new float[maxcount * TOTAL_COMPONENT_COUNT])
		, _maxcount(maxcount), _curcount(0), _nextparticle(0), _bo(0) {

	}

	~particle_system() {
		delete[] _particlearr;
		if (_bo != 0)
			glDeleteBuffers(1, &_bo);
	}

	bool
	init() {
		glGenBuffers(1, &_bo);
		glBindBuffer(GL_ARRAY_BUFFER, _bo);
		glBufferData(GL_ARRAY_BUFFER, _maxcount * TOTAL_COMPONENT_BYTES, NULL, GL_DYNAMIC_DRAW);
		glBindBuffer(GL_ARRAY_BUFFER, 0);
		return true;
	}

	void
	addparticle(point pos, int color, vector dir, float starttime) {
		int startoffset = _nextparticle * TOTAL_COMPONENT_COUNT;
		int offset = startoffset;
		
		if (++_curcount > _maxcount) _curcount = _maxcount;
		if (++_nextparticle >= _maxcount) _nextparticle = 0;

		_particlearr[offset++] = pos.x;
		_particlearr[offset++] = pos.y;
		_particlearr[offset++] = pos.z;

		_particlearr[offset++] = (color >> 16 & 0xFF) / 255.0f;
		_particlearr[offset++] = (color >> 8 & 0xFF) / 255.0f;
		_particlearr[offset++] = (color & 0xFF) / 255.0f;		

		_particlearr[offset++] = dir.x;
		_particlearr[offset++] = dir.y;
		_particlearr[offset++] = dir.z;

		_particlearr[offset++] = starttime;

		glBindBuffer(GL_ARRAY_BUFFER, _bo);
		glBufferSubData(GL_ARRAY_BUFFER, startoffset * sizeof(float), TOTAL_COMPONENT_BYTES, &_particlearr[startoffset]);
		glBindBuffer(GL_ARRAY_BUFFER, 0);
	}

	void 
	binddata() {
		GLsizei stride = sizeof(float) * TOTAL_COMPONENT_COUNT;
		long long offset = 0;
		glBindBuffer(GL_ARRAY_BUFFER, _bo);
			glEnableVertexAttribArray(0);
			glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, stride, NULL);

			offset += 3 * sizeof(float);
			glEnableVertexAttribArray(1);
			glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, stride, (void*)offset);

			offset += 3 * sizeof(float);
			glEnableVertexAttribArray(2);
			glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, stride, (void*)offset);

			offset += 3 * sizeof(float);
			glEnableVertexAttribArray(3);
			glVertexAttribPointer(3, 1, GL_FLOAT, GL_FALSE, stride, (void*)offset);
		glBindBuffer(GL_ARRAY_BUFFER, 0);
	}

	void
	draw() {
		glDrawArrays(GL_POINTS, 0, _curcount);
	}
};
